Scenario #9690: A Global Admin Can Create a Read Only Api Key Subject

The endpoint-scope *:read makes an API-key read-only: it allows all GET endpoints under /api/, but nothing which changes data. With the global ADMIN role granted to its API_KEY subject, such an API-key can read everything, e.g. for reporting or monitoring, without any risk of modifying data.

Properties

Given

name value
subjectUuid a91c0009-0000-0000-0000-000000000009
subjectName readonly.key

Create the read-only API_KEY Subject

HTTP POST "/api/rbac/subjects" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  <<EOF
{
  "uuid" : "a91c0009-0000-0000-0000-000000000009",
  "name" : "readonly.key",
  "type" : "API_KEY",
  "scopes" : [ "*:read" ]
}
EOF
=> status: 201 CREATED a91c0009-0000-0000-0000-000000000009
{
  "uuid" : "a91c0009-0000-0000-0000-000000000009",
  "name" : "readonly.key",
  "organization" : "readonly",
  "type" : "API_KEY",
  "apiKey" : "hsak_readonly.key.ddbd72befd7e3103ae96c15a97d410ac149c1e08565cbcf2703b960042a205c6",
  "scopes" : [ "*:read" ],
  "expiresAt" : null
}

Prerequisite: Resolve the UUID of the global ADMIN role

The grant API needs the UUID of the role which we want to grant.

HTTP GET "/api/rbac/roles?name=rbac.global%23global%3AADMIN" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }`
=> status: 200 OK 
[ {
  "uuid" : "7ea7e6cb-16dd-4fb9-aa82-427df5f205d7", // globalAdminRoleUuidToGrant
  "object.uuid" : "6ec9aacc-2bd3-4678-a8f9-31e61eef3b0a",
  "objectTable" : "rbac.global",
  "objectIdName" : "global",
  "roleType" : "ADMIN",
  "roleName" : "rbac.global#6ec9aacc-2bd3-4678-a8f9-31e61eef3b0a:ADMIN",
  "roleIdName" : "rbac.global#global:ADMIN"
} ]

Grant the global ADMIN role to the API_KEY Subject

HTTP POST "/api/rbac/grants" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  -H 'Hostsharing-Assumed-Roles: rbac.global#global:ADMIN' \
  <<EOF
{
  "assumed" : true,
  "grantedRole.uuid" : "7ea7e6cb-16dd-4fb9-aa82-427df5f205d7", // globalAdminRoleUuidToGrant
  "granteeSubject.uuid" : "a91c0009-0000-0000-0000-000000000009"
}
EOF
=> status: 201 CREATED 7ea7e6cb-16dd-4fb9-aa82-427df5f205d7 // globalAdminRoleUuidToGrant

Verify the read-only API-key can use GET endpoints, e.g. list all memberships

HTTP GET "/api/hs/office/memberships" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
[
  {
    "uuid" : "b48bf53e-8f91-4009-bd9a-1b857c31657c", // Membership: M-3101000 - Test AG
    "partner" : {
      "uuid" : "3da9e2a0-95cc-4ea4-bb4c-e73ad571beb4", // Partner: P-31010 - Test AG
      "partnerNumber" : "P-31010",
      "partnerRel" : {
        "uuid" : "f1943189-716b-46ea-8218-fa169740db15", // partnerRelationUuid
        "anchor" : {
          "uuid" : "806e75fe-7894-4422-86be-783107f07f3f",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "00ddd2fc-8742-46e5-924c-77749d4c8ccc", // Person: Test AG
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Test AG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "351fc296-c0f1-459a-8730-4a65b9605cee", // Contact: Test AG - China
          "caption" : "Test AG - China",
          "postalAddress" : {
            "country" : "China",
            "province" : "Guangdong Province",
            "city" : "Dongguan City",
            "street" : "No.2 Commercial Second Street",
            "district" : "Niushan Wei Wu",
            "department" : "Executive Board",
            "building" : "Thi Chi Koh Building"
          },
          "emailAddresses" : {
            "main" : "norden@test-ag.example.org"
          },
          "phoneNumbers" : {
            "phone" : "++15 999 654321"
          }
        }
      },
      "details" : {
        "uuid" : "ec212097-b265-4392-b0ad-2806db2beb66",
        "registrationOffice" : "Registergericht Hamburg",
        "registrationNumber" : "1234567",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-3101000",
    "memberNumberSuffix" : "00",
    "validFrom" : "2020-10-15",
    "validTo" : "2023-12-31",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  {
    "uuid" : "44efcd04-e400-4134-a776-96fa9081a44d",
    "partner" : {
      "uuid" : "3b00b421-19be-4f54-af72-8071cd2db54b",
      "partnerNumber" : "P-10001",
      "partnerRel" : {
        "uuid" : "e23bb6dc-c4c4-4d38-9c61-cea341cd463d",
        "anchor" : {
          "uuid" : "806e75fe-7894-4422-86be-783107f07f3f",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "f726eb70-6779-41eb-a327-09e132a0cdd5", // Person: First GmbH
          "personType" : "LEGAL_PERSON",
          "tradeName" : "First GmbH",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "57cefbf1-4b36-48b3-8400-2e027035af9a", // Contact: contact-admin@firstcontact.example.com
          "caption" : "first contact",
          "postalAddress" : {
            "country" : "Germany"
          },
          "emailAddresses" : {
            "main" : "contact-admin@firstcontact.example.com"
          },
          "phoneNumbers" : {
            "phone_office" : "+49 123 1234567"
          }
        }
      },
      "details" : {
        "uuid" : "999bc911-fd9d-4299-aa18-77938c268c98",
        "registrationOffice" : "Hamburg",
        "registrationNumber" : "RegNo123456789",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-1000101",
    "memberNumberSuffix" : "01",
    "validFrom" : "2022-10-01",
    "validTo" : "2024-12-30",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  {
    "uuid" : "f28e564d-4596-4065-9e33-796886727efa",
    "partner" : {
      "uuid" : "a894e353-2b23-4286-ab07-fa53e91927e3", // partnerUuid
      "partnerNumber" : "P-10002",
      "partnerRel" : {
        "uuid" : "d18c0d3c-43a5-411c-8c9c-48a189ad16bc",
        "anchor" : {
          "uuid" : "806e75fe-7894-4422-86be-783107f07f3f",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "ddbe26a0-9504-492d-b4f2-f5acf7052e94",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.",
          "salutation" : null,
          "title" : null,
          "givenName" : "Peter",
          "familyName" : "Smith"
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "89d7987a-6f0b-459e-afd9-a8bf81ec2048",
          "caption" : "second contact",
          "postalAddress" : {
            "country" : "Germany"
          },
          "emailAddresses" : {
            "main" : "contact-admin@secondcontact.example.com"
          },
          "phoneNumbers" : {
            "phone_office" : "+49 123 1234567"
          }
        }
      },
      "details" : {
        "uuid" : "57781e0c-3ce2-42b6-a281-044de4f6e92a",
        "registrationOffice" : "Hamburg",
        "registrationNumber" : "RegNo123456789",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-1000202",
    "memberNumberSuffix" : "02",
    "validFrom" : "2022-10-01",
    "validTo" : "2025-12-31",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  "..."
]

Verify the read-only API-key cannot write, e.g. not upsert a subject

HTTP PUT "/api/rbac/subjects/a91c0009-0000-0000-0000-000000000009" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY" \
  <<EOF
{
  "name" : "hsh-never_updated",
  "type" : "USER"
}
EOF
=> status: 403 FORBIDDEN 
{
  "path" : "/api/rbac/subjects/a91c0009-0000-0000-0000-000000000009",
  "statusCode" : 403,
  "statusPhrase" : "Forbidden",
  "message" : "ERROR: [403] API-key scopes do not allow PUT /api/rbac/subjects/a91c0009-0000-0000-0000-000000000009"
}

generated on 2026-08-10 01:38:08 for branch